package com.haha.redis.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * Redis Lua脚本执行工具类
 * 支持：脚本文件、脚本字符串、SHA1方式执行
 * 兼容：Spring Data Redis (Jedis/Lettuce)
 */
@Component
@Slf4j
public class RedisLuaScriptUtil {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 执行指定的lua脚本返回执行结果
     * --KEYS[1]: 限流 key
     * --ARGV[1]: 限流窗口
     * --ARGV[2]: 当前时间戳（作为score）
     * --ARGV[3]: 阈值
     * --ARGV[4]: score 对应的唯一value
     *
     * @param redisScript
     * @param keys
     * @param args
     * @return
     */
    public Boolean execLimitLua(RedisScript<Long> redisScript, List<String> keys, String... args) {

        // 可变参数转数组
        String[] argsArray = args != null ? args : new String[0];
        try {
            Long execute = redisTemplate.execute(redisScript, keys, (Object[]) argsArray);
            if (Objects.isNull(execute)) {
                return false;
            }
            return Boolean.TRUE.equals(execute.intValue());
        } catch (Exception e) {
            log.error("user register redis execLimitLua fail! e:{}", e);
        }
        return false;
    }
    
    /**
     * 从类路径加载Lua脚本文件并执行
     * @param scriptPath 脚本文件路径（classpath相对路径）
     * @param keys 脚本涉及的KEYS列表
     * @param args 脚本涉及的ARGV参数列表
     * @param resultType 返回值类型
     * @return 脚本执行结果
     */
    public <T> T executeScriptFromFile(String scriptPath, List<String> keys, Object[] args, Class<T> resultType) {
        try {
            DefaultRedisScript<T> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(scriptPath)));
            redisScript.setResultType(resultType);
            return redisTemplate.execute(redisScript, keys, args);
        } catch (Exception e) {
            throw new RuntimeException("执行Lua脚本文件失败: " + scriptPath, e);
        }
    }
    
    /**
     * 直接执行Lua脚本字符串
     * @param scriptContent Lua脚本内容
     * @param keys 脚本涉及的KEYS列表
     * @param args 脚本涉及的ARGV参数列表
     * @param resultType 返回值类型
     * @return 脚本执行结果
     */
    public <T> T executeScriptString(String scriptContent, List<String> keys, Object[] args, Class<T> resultType) {
        try {
            RedisScript<T> redisScript = RedisScript.of(scriptContent, resultType);
            return redisTemplate.execute(redisScript, keys, args);
        } catch (Exception e) {
            throw new RuntimeException("执行Lua脚本字符串失败", e);
        }
    }
    
    /**
     * 使用SHA1校验和执行已加载的脚本
     * @param sha1 脚本的SHA1校验和
     * @param keys 脚本涉及的KEYS列表
     * @param args 脚本涉及的ARGV参数列表
     * @param resultType 返回值类型
     * @return 脚本执行结果
     */
    public <T> T executeScriptBySha1(String sha1, List<String> keys, Object[] args, Class<T> resultType) {
        try {
            RedisScript<T> redisScript = RedisScript.of(sha1);
            return redisTemplate.execute(redisScript, keys, args);
        } catch (Exception e) {
            throw new RuntimeException("通过SHA1执行Lua脚本失败: " + sha1, e);
        }
    }
    
    /**
     * 预加载Lua脚本到Redis服务器并返回SHA1（用于后续EVALSHA调用）
     * @param scriptContent Lua脚本内容
     * @return 脚本的SHA1校验和
     */
    public String loadScript(String scriptContent) {
        try {
            RedisScript<String> script = RedisScript.of(scriptContent, String.class);
            return redisTemplate.getConnectionFactory().getConnection().scriptLoad(script.getScriptAsString().getBytes());
        } catch (Exception e) {
            throw new RuntimeException("加载Lua脚本到Redis失败", e);
        }
    }
}